package queue

import (
	"errors"
	"fmt"
	"strings"
)

type tArrayQueue struct {
	items []interface{}
	capacity int
	rindex int
	windex int
	version int64
}

var gEmptyQueueError = errors.New("empty queue")

func NewArrayQueue(initSpace int) IQueue {
	if initSpace < 0 {
		initSpace = 0
	}
	c := initSpace*2

	return &tArrayQueue{
		items: make([]interface{}, c),
		capacity: c,
		rindex: 0,
		windex: 0,
		version: 0,
	}
}

func (me *tArrayQueue) Size() int {
	return me.windex - me.rindex
}

func (me *tArrayQueue) IsEmpty() bool {
	return me.Size() <= 0
}

func (me *tArrayQueue) IsNotEmpty() bool {
	return !me.IsEmpty()
}

func (me *tArrayQueue) Push(value interface{}) {
	me.ensureSpace(1)
	me.items[me.windex] = value
	me.windex++
	me.version++
}

func (me *tArrayQueue) ensureSpace(space int) {
	if me.remainingSpace() >= space {
		return
	}

	for ;me.remainingSpace()<space; {
		me.capacity = maxInt(me.capacity*2, me.capacity + 1)
	}

	newItems := make([]interface{}, me.capacity)
	p := 0
	for i := me.rindex;i < me.windex;i++ {
		newItems[p] = me.items[i]
		p++
	}

	me.items = newItems
	me.windex -= me.rindex
	me.rindex = 0
}

func maxInt(x,y int) int {
	if x >= y {
		return x
	}
	return y
}

func (me *tArrayQueue) remainingSpace() int {
	return me.capacity - me.windex
}

func (me *tArrayQueue) Poll() (error, interface{}) {
	if me.IsEmpty() {
		return gEmptyQueueError, nil
	}

	it := me.items[me.rindex]
	me.items[me.rindex] = nil
	me.rindex++

	if me.rindex >= me.capacity / 2 {
		p := 0
		for i := me.rindex;i < me.windex;i++ {
			me.items[p] = me.items[i]
			me.items[i] = nil
			p++
		}
		me.windex -= me.rindex
		me.rindex = 0
	}

	me.version++
	return nil, it
}


func (me *tArrayQueue) Peek() (error, interface{}) {
	if me.IsEmpty() {
		return gEmptyQueueError, nil
	}

	return nil, me.items[me.rindex]
}

func (me *tArrayQueue) Clear() {
	for i := me.rindex;i < me.windex;i++ {
		me.items[i] = nil
	}

	me.rindex = 0
	me.windex = 0
	me.version++
}

func (me *tArrayQueue) Iterator() IListIterator {
	return newArrayQueueIterator(me)
}

func (me *tArrayQueue) String() string {
	itemStrings := make([]string, me.Size())
	p := 0
	for i := me.rindex;i < me.windex;i++ {
		itemStrings[p] = fmt.Sprintf("%v", me.items[i])
		p++
	}

	return fmt.Sprintf("c=%v,r=%v,w=%v,s=%v,v=%v [%s]", me.capacity, me.rindex, me.windex, me.Size(), me.version, strings.Join(itemStrings, ","))
}